package uk.ac.manchester.cs.owl.ontologyrepositoryapi.tools;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;

import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.io.OWLXMLOntologyFormat;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAnnotation;
import org.semanticweb.owlapi.model.OWLAnnotationProperty;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.OWLOntologyStorageException;

import uk.ac.manchester.cs.owl.ontologyrepositoryapi.BioPortalOntologyRepository;
import uk.ac.manchester.cs.owl.ontologyrepositoryapi.MissingImportsTreatment;
import uk.ac.manchester.cs.owl.ontologyrepositoryapi.OntologyRepositoryEntry;

/**
 * Author: Matthew Horridge<br>
 * The University of Manchester<br>
 * Bio-Health Informatics Group<br>
 * Date: 01/02/2011
 */
public class DownloadOntologies {

    private static final String ONTOLOGIES_DIRECTORY_NAME = "eval/onto/bioportal";

    private static final String IMPORTS_DIRECTORY_NAME = "imports";

    private static final String FAILED_TO_LOAD_DIRECTORY_NAME = "failed-to-load";

    private static final String ONTOLOGY_METADATA_PREFIX = "http://owl.cs.manchester.ac.uk/ontology#";

    private static final IRI SOURCE_ONTOLOGY_IRI = IRI.create(ONTOLOGY_METADATA_PREFIX + "sourceOntology");
    
    private final String apiKey;
    
    
    public DownloadOntologies(String apiKey) {
    	this.apiKey = apiKey;
    }

    public void download() {
        BioPortalOntologyRepository repository = new BioPortalOntologyRepository(apiKey);
        int count = 0;
        for (OntologyRepositoryEntry entry : repository.getEntries()) {
            if (entry.isOWLCompatibleFormat() && !"ODGI".equals(entry.getAbbreviation())) {
                count++;
                System.out.println("Processing ontology " + count + " (" + entry.getAbbreviation() + ")");
                downloadOntology(entry);
            }
        }
        System.out.println("Downloaded " + count + " OWL ontologies");
    }

    private void downloadOntology(OntologyRepositoryEntry entry) {
        
        File ontologiesDirectory = new File(ONTOLOGIES_DIRECTORY_NAME);
        String entryName = getEntryName(entry);
        /*File entryDirectory = new File(ontologiesDirectory, entryName);
        if(entryDirectory.exists()) {
            System.out.println("\t\t" + entryDirectory + "Directory already exists. Skipping.");
            return;
        }*/
        File ontologyFile = new File(ontologiesDirectory, entryName.replace("\"", "") + ".owl.xml");
        
        //entryDirectory.mkdirs();
        try {
        	
        	 if (ontologyFile.exists()) {
             	
        		 System.out.println("\t\tOntology already exists. Skipping.");
        		 
        		 return;
             	//ontologyFile.delete();
             	//ontologyFile.createNewFile();
             }
        	
            OWLOntology ontology = entry.loadOntology(MissingImportsTreatment.IGNORE);
            OWLOntologyManager mergedManager = OWLManager.createOWLOntologyManager();
            OWLOntology mergedOntology = mergedManager.createOntology(ontology.getOntologyID());
            int anonOntologyCount = 0;
            for (OWLOntology ont : ontology.getImportsClosure()) {
                for (OWLAxiom ax : ont.getLogicalAxioms()) {
                    OWLDataFactory df = mergedManager.getOWLDataFactory();
                    OWLAnnotationProperty sourceOntologyProperty = df.getOWLAnnotationProperty(SOURCE_ONTOLOGY_IRI);
                    OWLAnnotation annotation;
                    if (ont.isAnonymous()) {
                        OWLLiteral anonName = df.getOWLLiteral("anonymous" + anonOntologyCount);
                        annotation = df.getOWLAnnotation(sourceOntologyProperty, anonName);
                        anonOntologyCount++;
                    }
                    else {
                        annotation = df.getOWLAnnotation(sourceOntologyProperty, ont.getOntologyID().getOntologyIRI());
                    }
                    OWLAxiom annotatedAxiom = ax.getAnnotatedAxiom(Collections.singleton(annotation));
                    mergedManager.addAxiom(mergedOntology, annotatedAxiom);
                }
            }
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(ontologyFile));
            OWLXMLOntologyFormat ontologyFormat = new OWLXMLOntologyFormat();
            ontologyFormat.setPrefix("ont", ONTOLOGY_METADATA_PREFIX);
            mergedManager.saveOntology(mergedOntology, ontologyFormat, bos);
            bos.close();

        }
        catch (OWLOntologyCreationException e) {
            saveException(entry, e);
        }
        catch (OWLOntologyStorageException e) {
            saveException(entry, e);
        }
        catch (FileNotFoundException e) {
            saveException(entry, e);
        }
        catch (IOException e) {
            saveException(entry, e);
        }
        catch (RuntimeException e) {
            saveException(entry, e);
        }

    }

    private String getEntryName(OntologyRepositoryEntry entry) {
        return entry.getDisplayName().replaceAll("\\W+", "-").toLowerCase();
    }


    private void saveException(OntologyRepositoryEntry repositoryEntry, Exception e) {
        try {
            File failedToLoad = new File(FAILED_TO_LOAD_DIRECTORY_NAME);
            failedToLoad.mkdirs();
            File exceptionFile = new File(failedToLoad, getEntryName(repositoryEntry) + "---loader-exception.txt");
            PrintWriter pw = new PrintWriter(exceptionFile);
            e.printStackTrace(pw);
            pw.flush();
            pw.close();
        }
        catch (FileNotFoundException e1) {
            e1.printStackTrace();
        }
    }
}
